//
// originalview.cpp
//
// The code maintaining the original view of the input images
//

#include <iostream>
#include "impressionist.h"
#include "ImpressionistUI.h"
#include "originalview.h"
#include "processVRML.h"

#ifndef WIN32
#define min(a, b)	( ( (a)<(b) ) ? (a) : (b) )
#endif

using namespace std;
 
static int		eventToDo;
static int		isAnEvent=0;
static bool		drawingLine=false;
static bool		bfifthPoint = false;
static int		dynamicColor;
static Point	startPt;
static Point	endPt;
static Point	movingPt;
static bool		validPoint = false; //use in kConnect
static int		corner;		//index of current corner
static int colors[6][3] = {{100,0,0}, {0,100,0},{0,0,100},{100,100,0},{0,100,100},{100,0,100}};

OriginalView::OriginalView(int			x, 
						   int			y, 
						   int			w, 
						   int			h, 
						   const char*	l)
							: Fl_Gl_Window(x,y,w,h,l)
{
	m_nWindowWidth	= w;
	m_nWindowHeight	= h;
	m_state = kNone;
	Fl::focus(this);
}

void OriginalView::draw()
{
	Fl::focus(this);	//need this to get keyboard event
	if(!valid())
	{
		glClearColor(0.7f, 0.7f, 0.7f, 1.0);

		// We're only using 2-D, so turn off depth 
		glDisable( GL_DEPTH_TEST );

		// Tell openGL to read from the front buffer when capturing
		// out paint strokes 
		glReadBuffer( GL_FRONT );
		ortho();

	}

	glClear( GL_COLOR_BUFFER_BIT );

	if (m_pDoc->picture) 
	{
		double tmp_drawWidth,tmp_drawHeight;
		tmp_drawWidth = m_pDoc->width * m_pDoc->m_scale;
		tmp_drawHeight = m_pDoc->height * m_pDoc->m_scale;
		double cur_windowHeight = min(VIEW_HEIGHT,(int)tmp_drawHeight);
		double cur_windowWidth = min(VIEW_WIDTH, (int)tmp_drawWidth);
		resizeWindow(min( VIEW_WIDTH,(int)tmp_drawWidth),min( VIEW_HEIGHT, (int)tmp_drawHeight));
		m_nWindowWidth=w();
		m_nWindowHeight=h();

		int drawWidth, drawHeight;
		GLvoid* bitstart;

		// we are not using a scrollable window, so ignore it
		Point scrollpos;	// = GetScrollPosition();
		scrollpos.x=scrollpos.y=0;

		drawWidth	= min( m_nWindowWidth, /*m_pDoc->m_nWidth*/(int)tmp_drawWidth );
		drawHeight	= min( m_nWindowHeight, /*m_pDoc->m_nHeight*/(int)tmp_drawHeight ); // modified by zhaohua for zooming
		//printf("drawWidth, drawHeight: %d %d\n",drawWidth,drawHeight);
		//printf("nWindow: %d %d\n",m_nWindowWidth,m_nWindowHeight);

		int	startrow	= cur_windowHeight/*m_pDoc->m_nHeight*/ - (scrollpos.y + drawHeight);
		if ( startrow < 0 ) 
			startrow = 0;

		bitstart = m_pDoc->picture + 3 * ((m_pDoc->width * startrow) + scrollpos.x);

		// just copy image to GLwindow conceptually
		glRasterPos2i( 0, 0);//m_nWindowHeight - drawHeight );
		glPixelStorei( GL_UNPACK_ALIGNMENT, 1 );
		glPixelStorei( GL_UNPACK_ROW_LENGTH, m_pDoc->width );
		glDrawBuffer( GL_BACK );
		glPixelZoom( (GLfloat)m_pDoc->m_scale,(GLfloat)m_pDoc->m_scale);
		glDrawPixels( m_pDoc->width, m_pDoc->height, GL_RGB, GL_UNSIGNED_BYTE, bitstart );  // (drawWidth,drawHeight,...)

		// draw axis lines
		for(int i=0;i<3;i++){
			for (vector<pair<Point,Point> >::iterator it = m_axis[i].begin(); it != m_axis[i].end(); it++){
				drawLine(it->first, it->second,i);
			}
		}

		// draw reference plane points
		for (int i =0; i<m_points.size() && i <5;i++){
			drawPoint(m_points[i]);
		}

		// draw dynamic lines
		if (drawingLine){
			if(movingPt.x==0 && movingPt.y ==0)		drawPoint(startPt,dynamicColor);
			else drawLine(startPt,movingPt,dynamicColor);
		}

		// draw the out of plane point
		if (bfifthPoint){
			drawLine (m_zin,m_zout, 4);
		}

		// draw vertical lines
		for (int i = 0; i < m_rootpts.size() && i<m_spacepts.size() ; i++){
			drawLine(m_rootpts[i],m_spacepts[i],3);
			drawPoint(m_spacepts[i],5,5);
		}

		// draw patch points
		for (vector<Point>::iterator it = m_patch.begin(); it != m_patch.end(); it++){
			drawPoint(*it,4,5);
		}

	}

	glFlush();
}

void OriginalView::refresh()
{
	redraw();
}

void OriginalView::resizeWindow(int	width, 
								int	height)
{
	resize(x(), y(), width, height);
	glLoadIdentity();
	glViewport (0, 0, width,height);
	ortho();
	redraw();
}

Point OriginalView::GetScrollPosition()
{
	Point pt;
	pt.x = this->x();
	pt.y = this->y();
	printf("OriginalView position: %lf %lf",pt.x,pt.y);
	return pt;
}

int OriginalView::handle(int event)
{
	if (!m_pDoc->m_scale) return 0;
	Point coord;
	coord.x= Fl::event_x()/m_pDoc->m_scale;
	coord.y= Fl::event_y()/m_pDoc->m_scale;
	//coord.y= m_pDoc->height - Fl::event_y()/m_pDoc->m_scale;
	if (!m_pDoc->in_valid_range(coord.y,coord.x))
		return 0;
	
	int key = 0;

	switch (event)
	{
	case FL_ENTER:
		redraw();
		break;
	case FL_PUSH:
		//handles mouse click here
		switch(m_state){
		case kX:
			dynamicColor =0;
			setLinePt(coord,0);
			break;
		case kY:
			dynamicColor=1;
			setLinePt(coord,1);
			break;
		case kZ:
			dynamicColor = 2;
			setLinePt(coord,2);
			break;
		case kPoint:
			dynamicColor = 3;
			if(m_points.size() < 4){
				m_points.push_back(coord);
				m_pDoc->m_pUI->updateLabel(m_points.size(),1);
				if (m_points.size() ==4)	m_state = kFifthPoint;
			}
			else m_state = kNone;
			break;
		case kFifthPoint:
			dynamicColor = 3;
			bfifthPoint = false;
			if(!drawingLine){
				drawingLine = true;
				startPt = coord;
				m_zin = coord;
			}
			else{
				drawingLine = false;
				m_zout =coord;
				m_state = kNone;
				m_pDoc->m_pUI->updateLabel(5,1);
				bfifthPoint = true;
			}
			break;
		case kVertical:
			dynamicColor = 3;
			movingPt.x =0;
			movingPt.y=0;
			printf("coord is %d %d", coord.x,coord.y);
			if(!drawingLine){
				drawingLine = true;
				startPt = coord;
				m_rootpts.push_back(coord);
			}
			else{
				drawingLine = false;
				m_spacepts.push_back(coord);
				//testing
				m_pDoc->calculateRealPosition(coord,m_rootpts.back() );
				//testing end
			}
			break;
		case kConnect:
			if(validPoint){
				if(m_patch.size()>=4)	m_patch.clear();
				m_patch.push_back(endPt);
				m_vrml.push_back( corner);	//vrml

				if(m_patch.size()==4){
					calPatch();
				}
			}
			break;
		default:
			break;
		}
		redraw();
		break;
	case FL_MOVE:
		//handles mouse move
		if(m_state==kConnect){
			validPoint = false;
/*			for (vector<Point>::iterator it = m_rootpts.begin(); it != m_rootpts.end(); it++){
				if(withinRadius(*it,coord))	{
					validPoint = true;	
					endPt = *it;
				}
			}*/
			int stupidcounter =0;
			for (vector<Point>::iterator it = m_spacepts.begin(); it != m_spacepts.end(); it++){
				
				if(withinRadius(*it,coord))		{
					validPoint = true;
					corner = stupidcounter;		//vrml
					endPt = *it;
				}	
				stupidcounter++;
			}
			if (validPoint)		window()->cursor(FL_CURSOR_HAND);
			else	window()->cursor(FL_CURSOR_DEFAULT);
		}

		if (drawingLine) movingPt = coord;
		redraw();
		break;
	case FL_KEYBOARD:
		key = Fl::event_key();
		
		if (key==65293)	//enter
		{
			//enter key
		}
		else if (key==61)	// '='
		{
			// perform zoom in 
			if (Fl::event_ctrl()){
				m_pDoc->m_scale += 0.25;
				m_pDoc->m_scale = min(2.0,m_pDoc->m_scale);
				redraw();
			}
		}

		else if (key==45)// '-'
		{
			// perform zoom out
			if(Fl::event_ctrl()){
				m_pDoc->m_scale -=0.25;
				m_pDoc->m_scale = max(0.5,m_pDoc->m_scale);
				redraw();
			}
		}

		break;
	default:
		return 0;
		break;
	}	
	return 1;
}

void OriginalView::drawPoint(Point pt)
{
	glPointSize (5.0);
	glBegin(GL_POINTS);
		glColor3b (0,100,0);
		glVertex2d(pt.x * m_pDoc->m_scale, m_pDoc->height-pt.y* m_pDoc->m_scale);
	glEnd();
}

void OriginalView::drawPoint(Point pt, int c)
{
	glPointSize (1.0);
	glBegin(GL_POINTS);
		glColor3b (colors[c][0],colors[c][1],colors[c][2]);
		glVertex2d(pt.x * m_pDoc->m_scale,m_pDoc->height-pt.y* m_pDoc->m_scale);
	glEnd();
}

void OriginalView::drawPoint(Point pt, int c, int size)
{
	glPointSize (size);
	glBegin(GL_POINTS);
		glColor3b (colors[c][0],colors[c][1],colors[c][2]);
		glVertex2d(pt.x * m_pDoc->m_scale,m_pDoc->height-pt.y* m_pDoc->m_scale);
	glEnd();
}

void OriginalView::drawLine(Point pt1, Point pt2, int c)
{
	glBegin(GL_LINES);
		glColor3b (colors[c][0],colors[c][1],colors[c][2]);
		glVertex2d(pt1.x * m_pDoc->m_scale, m_pDoc->height-pt1.y* m_pDoc->m_scale);
		glVertex2d(pt2.x * m_pDoc->m_scale, m_pDoc->height-pt2.y* m_pDoc->m_scale);
	glEnd();
}

void OriginalView::setLinePt(Point coord, int axisID){
	movingPt.x =0;
	movingPt.y=0;
			if(!drawingLine){
				startPt = coord;
				drawingLine = true;
			}
			else {
				endPt = coord;
				drawingLine = false;
				pair<Point,Point> temp (startPt,endPt);		//add line to data storage here
				m_axis[axisID].push_back(temp);
			}
}

void OriginalView::calAxis(){
		for(int i=0;i<3;i++){
			for (vector<pair<Point,Point> >::iterator it = m_axis[i].begin(); it != m_axis[i].end(); it++){
				Vector3d v1,v2;
				v1 << it->first.x,it->first.y,1;
				v2 << it->second.x,it->second.y,1;
				pair<Vector3d, Vector3d> temp (v1,v2);
				m_pDoc->lines[i].push_back(temp);
			}
		}	
		m_pDoc->calculateVanishingPoints();
}

void OriginalView::resetAxis(){
	for (int i =0; i<3;i++){
		m_axis[i].clear();
		m_pDoc->lines[i].clear();
	}
}

void OriginalView::calHomo(){
	int i = 0;
	for(vector<Point>::iterator it = m_points.begin(); it!= m_points.end();it++){
		if (i<4){
			m_pDoc->pp_x[i] = it->x;
			m_pDoc->pp_y[i] = it->y;
			i++;
		}
	}
	m_pDoc->t_x = m_zout.x;
	m_pDoc->t_y = m_zout.y;
	m_pDoc->b_x = m_zin.x;
	m_pDoc->b_y = m_zin.y;

	m_pDoc->calculateHomography();
	for (int j = 0; j < 4; j++){
		printf("%lf %lf\n", m_pDoc->p_x[j], m_pDoc->p_y[j]);
	}
		for (int j = 0; j < 4; j++){
		printf("%lf %lf\n", m_pDoc->pp_x[j], m_pDoc->pp_y[j]);
	}
}

void OriginalView::calPatch(){
	if(m_patch.size()!= 4) return;
	double* tempx = new double [4];
	double* tempy = new double [4];
	int counter = 0;
	for (vector<Point>::iterator it = m_patch.begin(); it!=m_patch.end(); it++){
		tempx[counter] = it->x;
		tempy[counter] = it->y;
		counter++;
	}
	m_pDoc->patch_pp_x.push_back(tempy);
	m_pDoc->patch_pp_y.push_back(tempx);	
	double* standardx = new double [4];
	double* standardy = new double [4];
	standardx[0]=0;standardx[1]=100;standardx[2]=100;standardx[3]=0;
	standardy[0]=0;standardy[1]=0;standardy[2]=100;standardy[3]=100;
	m_pDoc->patch_p_x.push_back(standardx);
	m_pDoc->patch_p_y.push_back(standardy);
	printf("Size is %d\n", m_pDoc->patch_p_x.size());
	printf("Standard x is %lf %lf %lf %lf\n", standardx[0],standardx[1],standardx[2],standardx[3]);
	printf("Standard y is %lf %lf %lf %lf\n", standardy[0],standardy[1],standardy[2],standardy[3]);
	for (int i =0;i< m_pDoc->patch_p_x.size(); i++){
				for (int j = 0; j < 4; ++j) {
			printf("%lf %lf %lf %lf\n", m_pDoc->patch_p_x[i][j], m_pDoc->patch_p_y[i][j], m_pDoc->patch_pp_x[i][j], m_pDoc->patch_pp_y[i][j]);
		}
	}
	//m_pDoc->calculatePatches();
}

string OriginalView::printCoord(Point pt){
	char str[20];
	sprintf(str,"(%d,%d)",pt.x,pt.y);
	string result = str;
	return result;
}

bool OriginalView::withinRadius(Point pt1, Point pt2){
	int radius = 10;
	int dis = (pt1.x - pt2.x)*(pt1.x - pt2.x) + (pt1.y - pt2.y)*(pt1.y - pt2.y);
	if (dis < radius * radius)	return true;
	else return false;
}

void OriginalView::exportPatch(){
	m_pDoc->calculatePatches();
	//vrml
	char fname[] = "mymodel.txt";
	VRMLmodel pvrml(fname);
	pvrml.initVRML();
	int stupidcounter = 0;
	printf("vrml size %d\n",m_vrml.size());
	for (int j = 0; j < m_vrml.size(); j+=4) {
		printf("stupid counter %d\n", stupidcounter);
		Vector3i* coordinates = new Vector3i [4];
		for(int i =0;i<4;i++){
			int index =m_vrml[j+i];
			coordinates[i] = m_pDoc->real_positions[index];
			printf("checking corner %d\n", m_vrml[j+i]);
			cout << i << " " <<coordinates[i] << endl;
		}
		pvrml.addPolygon(stupidcounter,coordinates);
		
		stupidcounter++;
	}
	pvrml.closeVRML();

}

void OriginalView::resetPatch(){
	m_rootpts.clear();
	m_spacepts.clear();
	m_patch.clear();
	m_pDoc->patch_p_x.clear();
	m_pDoc->patch_p_y.clear();
	m_pDoc->patch_pp_x.clear();
	m_pDoc->patch_pp_y.clear();
	resetGlobals();
	redraw();
}

void OriginalView::resetGlobals(){
	Point temp(0,0);
	drawingLine=false;
	bfifthPoint = false;
	startPt = temp;
	endPt= temp;
	movingPt= temp;
	validPoint = false; 
}

void OriginalView::resetPoints(){
	Point temp(0,0);
	m_zin = temp;
	m_zout = temp;
	m_points.clear();
	for (int i = 1; i < 6; i++){
		m_pDoc->m_pUI->updateLabel(i,0);
	}
	resetGlobals();
	redraw();
}